<!--
 * @Title:
 * @Descripttion:
 * @Version: 2.0
 * @Author: tangyong
 * @Date: 2025-04-08 13:48:15
 * @LastEditors: weidewei
 * @LastEditTime: 2025-07-03 11:10:31
-->
<template>
  <div class="comp-dynamic-table" v-if=canEdit>
    <el-form ref="formInstance" :model="formData" @submit.prevent>
      <table ref="tableRef" class="el-table-simple">
        <tbody>
          <tr v-for="(item, index) in tableHeaderRules" :key="`header-${index}`">
            <td
              v-for="(col, idx) in item"
              :class="{ 'is-operate-column': col.value === '操作' }"
              :key="idx"
              :colspan="col.colSpan"
              :rowspan="col.rowSpan"
              :style="getTdStyle(col.colObj)"
            >
              <span :style="getShowStyle(col.colObj)" v-if="col.type === 'text'" class="cell-span">
                <el-tooltip
                  v-if="col.tipContent"
                  effect="dark"
                  :content="col.tipContent"
                  raw-content
                  placement="right"
                >
                  <el-icon color="#E6A23C"><Opportunity /></el-icon>
                </el-tooltip>
                <el-link
                  v-if="!col.props.disabled && col.value === '操作'"
                  type="primary"
                  :underline="false"
                  :icon="CirclePlus"
                  style="width: auto; font-size: 16px; vertical-align: text-bottom"
                  @click="handleRowAddClick"
                >
                </el-link>
                {{ col.value }}
              </span>
            </td>
          </tr>
          <tr v-for="(item, index) in formData.tableData" :key="`data-${index}`">
            <td
              v-for="(col, idx) in tableColumns"
              :class="{ 'is-operate-column': col.type === 'link' }"
              :key="`col-${idx}`"
              :colspan="col.colSpan"
              :rowspan="col.rowSpan"
              :style="getTdStyle(col.colObj)"
            >
              <el-form-item :prop="`tableData.${index}.${col.prop}`" :rules="getRule(col)">
                <!-- 纯文本 -->
                <span v-if="col.type === 'text'" class="cell-span">
                  <el-tooltip
                    v-if="col.tipContent"
                    effect="dark"
                    :content="col.tipContent"
                    raw-content
                    placement="right"
                  >
                    <el-icon color="#E6A23C"><Opportunity /></el-icon>
                  </el-tooltip>
                  {{ col.value }}
                </span>
                <!-- 下拉框 -->
                <comp-el-select
                  v-else-if="col.type === 'select'"
                  v-model="item[col.prop]"
                  clearable
                  v-bind="getColumnProps(col)"
                  style="width: 100%"
                  v-on="getColumnEvents(col)"
                  @blur="showValidTip(col, index)"
                />
                <!-- 级联 -->
                <!-- <comp-dict-cascader
                  v-else-if="col.type === 'dict-cascader'"
                  v-model="item[col.prop]"
                  clearable
                  v-bind="getColumnProps(col)"
                  v-on="getColumnEvents(col)"
                  @blur="showValidTip(col, index)"
                /> -->
                <!-- 输入框 -->
                <el-input
                  v-else-if="col.type === 'input'"
                  v-model="item[col.prop]"
                  clearable
                  v-bind="getColumnProps(col)"
                  v-on="getColumnEvents(col)"
                  @blur="showValidTip(col, index)"
                  @click.stop
                />
                <!-- 数字输入框 -->
                <el-input-number
                  v-else-if="col.type === 'number-input'"
                  v-model="item[col.prop]"
                  clearable
                  v-bind="getColumnProps(col)"
                  v-on="getColumnEvents(col)"
                  @blur="showValidTip(col, index)"
                >
                  <template v-if="col.suffix" #append>{{ col.suffix }}</template>
                </el-input-number>
                <!-- 日期组件 -->
                <el-date-picker
                  v-else-if="col.type === 'date'"
                  v-model="item[col.prop]"
                  :type="getColumnProps(formData)?.type || 'date'"
                  v-bind="getColumnProps(col)"
                  v-on="getColumnEvents(col)"
                  @blur="showValidTip(col, index)"
                  placeholder="选择日期"
                  value-format="YYYY/MM/DD"
                />
                <el-date-picker
                  v-else-if="col.type === 'daterange'"
                  v-model="item[col.prop]"
                  :type="getColumnProps(col)?.type || 'daterange'"
                  v-bind="getColumnProps(col)"
                  v-on="getColumnEvents(col)"
                  start-placeholder="开始日期"
                  end-placeholder="结束日期"
                  range-separator="-"
                  value-format="x"
                />
                <template v-else-if="col.type === 'file'">
                  <div v-if="!isEmptyObj(item[col.prop])" class="table-file-list">
                    <span class="file-name">
                      <el-tooltip placement="top" :content="item[col.prop]['fileName']">
                        {{ item[col.prop]["fileName"] }}
                      </el-tooltip>
                    </span>
                    <el-link
                      type="primary"
                      :underline="false"
                      title="查看"
                      style="margin-left: 0.05rem"
                      @click="handleFilePreviewClick(col)"
                      ><i class="iconfont icon-preview"></i
                    ></el-link>
                    <el-link
                      type="primary"
                      :underline="false"
                      title="下载"
                      @click.stop="handleFileDownloadClick(col)"
                      ><i class="iconfont icon-download"></i
                    ></el-link>
                    <el-link
                      type="danger"
                      :underline="false"
                      title="删除"
                      @click.stop="handleFileDeleteClick(col)"
                      ><i class="iconfont icon-dustbin"></i
                    ></el-link>
                  </div>
                  <el-link
                    v-if="canEdit"
                    type="primary"
                    :underline="false"
                    @click="handleUploladClick(col)"
                    ><i class="iconfont icon-upload" style="font-size: 0.2rem"></i>上传</el-link
                  >
                </template>
                <template v-else-if="col.type === 'link'">
                  <el-link type="danger" :underline="false" @click="handleRowDeleteClick(index)"
                    >删除</el-link
                  >
                </template>
              </el-form-item>
            </td>
          </tr>
        </tbody>
      </table>
    </el-form>
    <!-- <dialog-upload
      v-if="dialogStatus"
      v-model:dialog-status="dialogStatus"
      :upload-props="{
        limit: 1
      }"
      @dialog-confirm="handleDialogConfirm"
    /> -->
  </div>
</template>
<script lang="ts" setup>
  import { isEmptyObj, isEmpty } from "@/utils/index";
  // import { filePreview, fileDownload } from "@utils/file";
  import { confirm } from "@/utils/index";
  import { cloneDeep } from "lodash-es";

  import { Opportunity, CirclePlus } from "@element-plus/icons-vue";
  // const DialogUpload = defineAsyncComponent(
  //   () => import("@components/CompElUpload/DialogUpload.vue")
  // );

  const props = defineProps({
    rules: {
      type: Array,
      default: () => []
    },
    data: {
      type: Array,
      default: () => []
    },
    startRow: {
      type: Number,
      default: 1
    },
    canEdit: {
      type: Boolean,
      default: true
    }
  });

  const { proxy } = getCurrentInstance()!;

  const formInstance: any = shallowRef(null);
  const formData: any = ref({
    tableData: [{}]
  });
  const tableHeaderRules: any = ref([]);
  const tableColumns: any = ref([]);
  const dialogStatus = ref(false);

  watch(
    () => props.rules,
    (arr) => {
      if (!Array.isArray(arr) || !arr.length) return;
      nextTick(() => {
        initRules();
      });
    },
    { immediate: true }
  );

  function initRules() {
    let originRules = cloneDeep(props.rules);
    const exposed: any = proxy?.$.exposed;
    originRules.forEach((item: any, index: any) => {
      item.on = {
        change: () => {
          nextTick(() => {
            dispatchEvent();
          });
        }
      };
      if (Array.isArray(item.validate) && item.validate.length) {
        item.validate.forEach((it) => {
          if (it.type === "fn") {
            it.validator =
              typeof exposed[it.validator] === "function" ? exposed[it.validator] : function () {};
          }
        });
      }
    });
    // const groupRules: any = originRules.reduce((acc: any, cur: any) => {
    //   if (cur.startRow) {
    //     acc.push([cur]);
    //   } else {
    //     acc[acc.length - 1].push(cur);
    //   }
    //   return acc;
    // }, []);

    const groupRules = (originRules) => {
      return originRules.reduce((acc, cur) => {
        const cellRow = cur.colObj.cellRow;
        if (!acc[cellRow]) {
          acc[cellRow] = []; // 如果该行不存在，初始化一个空数组
        }
        acc[cellRow].push(cur); // 将当前项推入对应的行数组
        return acc;
      }, {});
    };

    const groupedData = groupRules(originRules);
    const resultData = Object.values(groupedData); // 转换为二维数组
    // 分离静态表头与动态数据表头
    tableHeaderRules.value = resultData.slice(0, props.startRow);
    const _tableColumns = resultData.slice(props.startRow, props.startRow + 1);
    if (Array.isArray(_tableColumns)) {
      const uniqueTableColumns = _tableColumns[0];
      tableColumns.value = removeDuplicates(uniqueTableColumns);
    } else {
      tableColumns.value = [];
    }
    console.log('tableHeaderRules',tableHeaderRules.value)
    // tableColumns.value = Array.isArray(_tableColumns) ? _tableColumns[0] : [];
    if (!props.data.length) {
      formData.value.tableData = cloneDeep([{}]);
    } else {
      formData.value.tableData = cloneDeep(props.data);
    }
  }

  function removeDuplicates(arr) {
    const seen = {};
    return arr.reduce((result, item) => {
      const key = `${item.prop}-${item.type}`;
      if (!seen[key]) {
        seen[key] = true;
        result.push(item);
      }
      return result;
    }, []);
  }

  function getRule(col) {
    if (Array.isArray(col.validate) && col.validate.length) return col.validate;
    return [];
  }

  function getColumnProps(column) {
    return typeof column.props === "object" ? column.props : {};
  }

  function getColumnEvents(column) {
    if (isEmptyObj(column.on)) return {};
    const tempEvt = {};
    Object.keys(column.on).forEach((evtName) => {
      tempEvt[evtName] = column.on[evtName];
    });
    return tempEvt;
  }

  function dispatchEvent() {
    formInstance.value.clearValidate();
  }

  function excuteFormula(params, prop?, dataIndex?) {
    if (isEmpty(params.digit)) return;
    var digit = params.digit ? params.digit : 2;
    function caculateExpress(params, prop, dataIndex) {
      if (isEmpty(params?.express)) return;
      if (isEmpty(params?.fields)) return;
      if (isEmpty(params.resField)) return;
      if (isEmpty(params.digit)) return;
      try {
        var fields = params.fields;
        //
        var resField = params.resField;
        var express = params.express;
        var digit = params.digit ? params.digit : 2;
        const variables = {};
        for (const item of fields) {
          if (isEmpty(formData.value.tableData[dataIndex][item])) {
            return false;
          }
          variables[item] = parseFloat(formData.value.tableData[dataIndex][item]) || 0;
        }
        const safeExpress = express.replace(/\b([a-zA-Z_]\w*)\b/g, (match) => `variables.${match}`);
        const calculate = new Function("variables", `return ${safeExpress}`);
        const expressValue = parseFloat(calculate(variables));
        return expressValue;
      } catch (e) {
        proxy?.$showToast("配置错误", "error");
      }
    }
    const ret: any = caculateExpress(params, prop, dataIndex);
    if (ret === false) return;
    if (isNaN(ret) || ret == Infinity || ret == -Infinity) {
      formData.value.tableData[dataIndex][prop] = 0;
    } else {
      formData.value.tableData[dataIndex][prop] = ret.toFixed(digit);
    }
  }

  // function caculateExpress(params, prop?,dataIndex?) {
  //   if (isEmpty(params?.express)) return;
  //   if (isEmpty(params?.fields)) return;
  //   if (isEmpty(params.resField)) return;
  //   if (isEmpty(params.digit)) return;
  //   try {
  //     var fields = params.fields;
  //     //
  //     var resField = params.resField;
  //     var express = params.express;
  //     var digit = params.digit ? params.digit : 2;
  //     const variables = {};
  //     for (const item of fields) {
  //       if (isEmpty(formData.value.tableData[dataIndex][item])) {
  //         return false;
  //       }
  //       variables[item] = parseFloat(formData.value.tableData[dataIndex][item]) || 0;
  //     }
  //     const safeExpress = express.replace(/\b([a-zA-Z_]\w*)\b/g, (match) => `variables.${match}`);
  //     const calculate = new Function("variables", `return ${safeExpress}`);
  //     const expressValue = parseFloat(calculate(variables));
  //     return expressValue;
  //   } catch (e) {
  //
  //     proxy?.$showToast("配置错误", "error");
  //   }
  // }

  // 比较相等
  function comppareEqual(params, prop, dataIndex) {
    function caculateExpress(params, prop, dataIndex) {
      if (isEmpty(params?.express)) return;
      if (isEmpty(params?.fields)) return;
      if (isEmpty(params.resField)) return;
      if (isEmpty(params.digit)) return;
      try {
        var fields = params.fields;
        //
        var resField = params.resField;
        var express = params.express;
        var digit = params.digit ? params.digit : 2;
        const variables = {};
        for (const item of fields) {
          if (isEmpty(formData.value.tableData[dataIndex][item])) {
            return false;
          }
          variables[item] = parseFloat(formData.value.tableData[dataIndex][item]) || 0;
        }
        const safeExpress = express.replace(/\b([a-zA-Z_]\w*)\b/g, (match) => `variables.${match}`);
        const calculate = new Function("variables", `return ${safeExpress}`);
        const expressValue = parseFloat(calculate(variables));
        return expressValue;
      } catch (e) {
        proxy?.$showToast("配置错误", "error");
      }
    }
    const ret: any = caculateExpress(params, prop, dataIndex);
    if (ret === false) return;
    if (isNaN(ret) || ret == Infinity || ret == -Infinity) {
      formData.value.tableData[dataIndex][prop] = "";
      proxy?.$showToast("公式校验错误", "error");
    } else if (parseFloat(ret.toFixed(4)) !== 0) {
      formData.value.tableData[dataIndex][prop] = "";
      proxy?.$showToast("公式校验错误", "error");
    }
  }

  // 比较不等
  function compareUnequal(params, prop, dataIndex) {
    function caculateExpress(params, prop, dataIndex) {
      if (isEmpty(params?.express)) return;
      if (isEmpty(params?.fields)) return;
      if (isEmpty(params.resField)) return;
      if (isEmpty(params.digit)) return;
      try {
        var fields = params.fields;
        //
        var resField = params.resField;
        var express = params.express;
        var digit = params.digit ? params.digit : 2;
        const variables = {};
        for (const item of fields) {
          if (isEmpty(formData.value.tableData[dataIndex][item])) {
            return false;
          }
          variables[item] = parseFloat(formData.value.tableData[dataIndex][item]) || 0;
        }
        const safeExpress = express.replace(/\b([a-zA-Z_]\w*)\b/g, (match) => `variables.${match}`);
        const calculate = new Function("variables", `return ${safeExpress}`);
        const expressValue = parseFloat(calculate(variables));
        return expressValue;
      } catch (e) {
        proxy?.$showToast("配置错误", "error");
      }
    }
    const ret: any = caculateExpress(params, prop, dataIndex);
    if (ret === false) return;
    if (isNaN(ret) || ret == Infinity || ret == -Infinity) {
      formData.value.tableData[dataIndex][prop] = "";
      proxy?.$showToast("公式校验错误", "error");
    } else if (parseFloat(ret.toFixed(4)) === 0) {
      formData.value.tableData[dataIndex][prop] = "";
      proxy?.$showToast("公式校验错误", "error");
    }
  }

  // 比较大于或等于
  function compareGreaterThanOrEqual(params, prop, dataIndex) {
    function caculateExpress(params, prop, dataIndex) {
      if (isEmpty(params?.express)) return;
      if (isEmpty(params?.fields)) return;
      if (isEmpty(params.resField)) return;
      if (isEmpty(params.digit)) return;
      try {
        var fields = params.fields;
        //
        var resField = params.resField;
        var express = params.express;
        var digit = params.digit ? params.digit : 2;
        const variables = {};
        for (const item of fields) {
          if (isEmpty(formData.value.tableData[dataIndex][item])) {
            return false;
          }
          variables[item] = parseFloat(formData.value.tableData[dataIndex][item]) || 0;
        }
        const safeExpress = express.replace(/\b([a-zA-Z_]\w*)\b/g, (match) => `variables.${match}`);
        const calculate = new Function("variables", `return ${safeExpress}`);
        const expressValue = parseFloat(calculate(variables));
        return expressValue;
      } catch (e) {
        proxy?.$showToast("配置错误", "error");
      }
    }
    const ret: any = caculateExpress(params, prop, dataIndex);
    console.log("ret", ret);
    if (ret === false) return;
    if (isNaN(ret) || ret == Infinity || ret == -Infinity) {
      formData.value.tableData[dataIndex][prop] = "";
      proxy?.$showToast("公式校验错误", "error");
    } else if (parseFloat(ret.toFixed(4)) < 0) {
      formData.value.tableData[dataIndex][prop] = "";
      proxy?.$showToast("公式校验错误", "error");
    }
  }

  // 比较是否小于1
  function compareLessThanOne(params, prop, dataIndex) {
    function caculateExpress(params, prop, dataIndex) {
      if (isEmpty(params?.express)) return;
      if (isEmpty(params?.fields)) return;
      if (isEmpty(params.resField)) return;
      if (isEmpty(params.digit)) return;
      try {
        var fields = params.fields;
        //
        var resField = params.resField;
        var express = params.express;
        var digit = params.digit ? params.digit : 2;
        const variables = {};
        for (const item of fields) {
          if (isEmpty(formData.value.tableData[dataIndex][item])) {
            return false;
          }
          variables[item] = parseFloat(formData.value.tableData[dataIndex][item]) || 0;
        }
        const safeExpress = express.replace(/\b([a-zA-Z_]\w*)\b/g, (match) => `variables.${match}`);
        const calculate = new Function("variables", `return ${safeExpress}`);
        const expressValue = parseFloat(calculate(variables));
        return expressValue;
      } catch (e) {
        proxy?.$showToast("配置错误", "error");
      }
    }
    const ret: any = caculateExpress(params, prop, dataIndex);
    if (ret === false) return;
    if (isNaN(ret) || ret == Infinity || ret == -Infinity) {
      formData.value.tableData[dataIndex][prop] = "";
      proxy?.$showToast("公式校验错误", "error");
    } else if (Math.abs(parseFloat(ret.toFixed(4))) >= 1) {
      formData.value.tableData[dataIndex][prop] = "";
      proxy?.$showToast("公式校验错误", "error");
    }
  }

  function handleFilePreviewClick(col) {
    if (isEmptyObj(formData.value[col.prop])) return proxy?.$showToast("附件id为空！", "warning");
    // filePreview(formData.value[col.prop]["id"]);
  }

  function handleFileDownloadClick(col) {
    if (isEmptyObj(formData.value[col.prop])) return proxy?.$showToast("附件id为空！", "warning");
    // fileDownload({ fileId: formData.value[col.prop]["id"] });
  }

  function handleFileDeleteClick(col) {
    confirm(() => {
      formData.value[col.prop] = undefined;
    });
  }

  let selectedCol: any = null;
  function handleUploladClick(col) {
    selectedCol = col;
    dialogStatus.value = true;
  }

  function handleDialogConfirm({ fileList }) {
    if (!selectedCol || !fileList.length) return;
    formData.value[selectedCol.prop] = {
      fileName: fileList[0].fileName,
      id: fileList[0].fileId
    };
  }

  // 行删除
  function handleRowDeleteClick(index) {
    confirm(() => {
      formData.value.tableData.splice(index, 1);
    });
  }

  // 行新增
  async function handleRowAddClick() {
    const valid = await validate();
    if (!valid) return;
    formData.value.tableData.push({});
  }

  function validate() {
    return new Promise((resolve) => {
      formInstance.value.validate((valid) => {
        if (valid) {
          resolve(cloneDeep(formData.value));
        } else {
          resolve(false);
        }
      });
    });
  }

  function showValidTip(rule, index) {
    const col = rule.colObj;
    const dataIndex = index;
    const row = formData.value.tableData[index];
    // 必填检验
    // if (rule.required && isEmpty(row[rule.prop])) {
    //   proxy?.$showToast(col.inputTip, "error");
    // }

    // // 正则校验
    if (rule.checkRule) {
      const inputVal = row[rule.prop];
      if (inputVal) {
        const isPassReg = new RegExp(rule.checkRule.reg).test(inputVal);
        if (!isPassReg) {
          proxy?.$showToast(col.inputTip, "error");
        }
      }
    }
    tableColumns.value.forEach((item: any) => {
      if (Array.isArray(item.emit) && item.emit.length) {
        item.emit.forEach((it) => {
          if (it.fnName === "excuteFormula") {
            excuteFormula(it.params, item.prop, dataIndex);
          }
          if (it.fnName === "comppareEqual" && it.params.fields.includes(rule.prop)) {
            comppareEqual(it.params, rule.prop, dataIndex);
          }
          if (it.fnName === "compareUnequal" && it.params.fields.includes(rule.prop)) {
            compareUnequal(it.params, rule.prop, dataIndex);
          }
          if (it.fnName === "compareGreaterThanOrEqual" && it.params.fields.includes(rule.prop)) {
            compareGreaterThanOrEqual(it.params, rule.prop, dataIndex);
          }
          if (it.fnName === "compareLessThanOne" && it.params.fields.includes(rule.prop)) {
            compareLessThanOne(it.params, rule.prop, dataIndex);
          }
        });
      }
    });
  }

  function getShowStyle(colObj) {
    try {
      return colObj.cellStyle ? JSON.parse(colObj.cellStyle) : {};
    } catch {
      return {}; // 解析失败时返回空对象
    }
  }

  function getTdStyle(colObj) {
    return {
      width: colObj.colWidth || "150px",
      minWidth: colObj.colWidth || "150px",
      maxWidth: colObj.colWidth || "150px",
      height: colObj.rowHeight || "auto",
      minHeight: colObj.rowHeight || "auto",
      maxHeight: colObj.rowHeight || "auto"
    };
  }

  function getFormData() {
    return formData.value;
  }

  defineExpose({
    validate,
    excuteFormula,
    comppareEqual,
    compareUnequal,
    compareGreaterThanOrEqual,
    compareLessThanOne,
    getFormData
  });
</script>

<style lang="scss" scoped>
  table {
    table-layout: auto;
  }
  .el-table-simple {
    table-layout: auto;
    tbody {
      border-top: 1px solid #e3e8ee;
      // td {
      //   min-width: 150px;
      //   max-width: 150px;
      //   width: 150px;
      // }
      .is-operate-column {
        width: 90px;
        text-align: center;
      }
    }

    :deep(.el-form-item) {
      margin-bottom: 0;

      .el-form-item__content {
        .cell-span {
          width: 100%;
          padding: 5px 0;
          line-height: 18px;
        }
        .el-link {
          width: 100%;
        }
      }
    }
  }
    .el-table-simple {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
  color: #606266;
  thead {
    color: #5d6476;
    th {
      font-weight: normal;
      text-align: center;
      border-top: 1px solid #e3e8ee;
      background: #eef1f6;
    }
  }
  tbody {
    font-size: 14px;
    tr {
      transition: background 0.2s;
      &:hover {
        background: #eaf1fb;
      }
    }
  }
  td,
  th {
    padding: 6px 10px;
    line-height: 23px;
    word-break: break-all;
    border-right: 1px solid #e3e8ee;
    border-bottom: 1px solid #e3e8ee;
    &:first-child {
      border-left: 1px solid #e3e8ee;
    }
    .el-icon {
      vertical-align: text-bottom;
    }
  }
}
</style>
